<?php

namespace ApiSdk\TikTokShop\Servers;

use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Exception;
use GuzzleHttp\RequestOptions;
use Illuminate\Config\Repository;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Log;


class BaseService
{
    /**
     * 请求类型
     */
    const METHOD_GET    = 'GET';
    const METHOD_POST   = 'POST';
    const METHOD_PUT    = 'PUT';
    const METHOD_DELETE = 'DELETE';

    private $appKey;   //应用程序ID
    private $appSecret;//客户ID
    private $token;
    private $shopId;
    private $base_url;
    private $apiParams;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             //api请求参数
    private $headerParams  = [];                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    //头部请求信息
    private $commonParams  = [];
    private $jsonParams    = [];
    private $_httpClient;
    private $maxRetryTimes = 0;
    private $retryInterval = 1;
    private $http_errors   = false;
    private $sandboxType   = 0;
    private $auth_base_url = '';
    private $site          = '';

    private $version = '';
    /**
     * @throws Exception
     */
    function __construct($site = 'global')
    {
        $this->sandboxType = config('platform.TikTok.sandbox_type', 0);

        if ($this->sandboxType == 1) {
            $this->base_url      = config('platform.TikTok.sandbox_base_url', 'https://open-api-sandbox.tiktokglobalshop.com');
            $this->auth_base_url = config('platform.TikTok.auth_sandbox_base_url', 'https://auth-sandbox.tiktok-shops.com');
        } else {
            $this->base_url      = config('platform.TikTok.base_url', 'https://open-api.tiktokglobalshop.com');
            $this->auth_base_url = config('platform.TikTok.auth_base_url', 'https://auth.tiktok-shops.com');
        }

        $this->appKey    = config('platform.TikTok.appKey');
        $this->appSecret = config('platform.TikTok.appSecret');

        $this->_httpClient = new Client([
                                            'base_uri' => $this->base_url
                                        ]);
        $this->setMaxRetryTimes(config('platform.TikTok.maxRetryTimes', 1));
        $this->setRetryInterval(config('platform.TikTok.retryInterval', 1));
    }



    /**
     * Notes: 设置错误返回信息
     * author: lfy
     * Date: 2021/10/11
     * Time: 10:58
     * @param bool $type
     * @return $this
     */
    public function setHttpErrors(bool $type): BaseService
    {
        $this->http_errors = $type;
        return $this;
    }

    /**
     * 設置站點
     * @param $site
     * @return $this
     */
    public function setSite($site): BaseService
    {
        $this->site = strtolower($site);
        if (in_array($this->site,['tiktok_us','us'])) {
            $this->appKey        = config('platform.TikTok.appKeyUs');
            $this->appSecret     = config('platform.TikTok.appSecretUs');
            $this->base_url      = config('platform.TikTok.base_us_url', $this->base_url);
            $this->auth_base_url = config('platform.TikTok.auth_base_us_url', $this->auth_base_url);
        }
        return $this;
    }
    /**
     * Notes: 获取授权地址
     * author: lfy
     * Date: 2021/10/11
     * Time: 10:40
     * @param $state
     * @return string
     */
    public function getAuthorizedUrl($state): string
    {
        return $this->auth_base_url . "/oauth/authorize?app_key={$this->appKey}&state=${state}";
    }

    public function setVersion($version=''){
        $this->version = $version;
        return $this;
    }

    /**
     * @Notes : 设置授权请求默认域
     * @return $this
     * @author : Lfy
     * @Time : 2022-04-07   10:24
     */
    public function setAuthBaseUrl(): BaseService
    {
        $this->base_url    = $this->auth_base_url;
        $this->_httpClient = new Client(['base_uri' => $this->base_url]);
        return $this;
    }

    /**
     * Notes: 设置token
     * author: lfy
     * Date: 2021/10/11
     * Time: 10:36
     * @param $token
     * @return $this
     */
    public function setToken($token): BaseService
    {
        $this->token = $token;
        return $this;
    }

    /**
     * @Notes : 设置店铺id
     * @param $shopId
     * @return $this
     * @author : Lfy
     * @Time : 2022-04-07   13:57
     */
    public function setShopId($shopId): BaseService
    {
        $this->shopId = $shopId;
        return $this;
    }

    /**
     * Notes:
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:31
     * @param int $maxRetryTimes
     */
    public function setMaxRetryTimes(int $maxRetryTimes): void
    {
        if ($maxRetryTimes < 0) {
            $maxRetryTimes = 0;
        } else if ($maxRetryTimes > 10) $maxRetryTimes = 10;
        $this->maxRetryTimes = $maxRetryTimes;
    }

    /**
     * Notes:
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:31
     * @param int $retryInterval
     */
    public function setRetryInterval(int $retryInterval): void
    {
        if ($retryInterval < 0) {
            $retryInterval = 0;
        }
        $this->retryInterval = $retryInterval;
    }


    /**
     * Notes: 设置单个请求参数
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:31
     * @param string $key
     * @param $value
     */
    public function addApiParam(string $key, $value)
    {
//        if (is_object($value) || is_array($value)) $value = json_encode($value, JSON_UNESCAPED_UNICODE);
        $this->apiParams[$key] = $value;
    }


    public function addCommonParams(string $key, $value)
    {
//        if (is_object($value) || is_array($value)) $value = json_encode($value, JSON_UNESCAPED_UNICODE);
        $this->commonParams[$key] = $value;
    }

    /**
     * @param $value
     * @return $this
     */
    public function setShopCipher($value): BaseService
    {
        if (!empty($value)) $this->commonParams['shop_cipher'] = $value;
        return $this;
    }

    /**
     * Notes: 批量设置请求参数
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:31
     * @param array $apiParams
     * @return $this
     */
    public function setApiParams(array $apiParams): BaseService
    {
        foreach ($apiParams as $paramKey => $apiParam) $this->addApiParam($paramKey, $apiParam);
        unset($paramKey, $apiParam);
        return $this;
    }


    /**
     * Notes: 设置当个头部请求信息
     * author: lfy
     * Date: 2021/10/8
     * Time: 16:19
     * @param string $key
     * @param string $value
     */
    public function addHeaderParam(string $key, string $value)
    {
        $this->headerParams[$key] = $value;
    }

    /**
     * Notes:   批量设置头部请求信息
     * author: lfy
     * Date: 2021/10/8
     * Time: 16:20
     * @param array $headerParams
     * @return $this
     */
    public function setHeaderParams(array $headerParams): BaseService
    {
        foreach ($headerParams as $key => $headerParam) {
            $this->addHeaderParam($key, $headerParam);
        }
        unset($key, $headerParam);

        return $this;
    }

    /**
     * Notes: 请求api
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:31
     * @return mixed|void
     * @throws GuzzleException
     */
    private function request(string $uri, $method = self::METHOD_POST, array $options = [])
    {
        try {

            $retryTimes = 0;
            while (true) try {
                if (!empty($this->headerParams)) $options['headers'] = $this->headerParams;

//                if (!($method !== self::METHOD_GET) || !($method !== self::METHOD_PUT)) {
//                    $options['query'] = $this->apiParams;
//                    $this->apiParams  = [];
//                }
//                if (!empty($this->apiParams)) $options['form_params'] = $this->commonParams;
                $options['http_errors'] = $this->http_errors;
                $response               = $this->_httpClient->request($method, $uri, $options);
                return $this->getResponse($response);
            } catch (Exception $e) {
                if ($retryTimes < $this->maxRetryTimes) {
                    $retryTimes += 1;
                    sleep($this->retryInterval);
                } else {
                    throw $e;
                }
            }
        } catch (Exception $ex) {
            $errorData = ['message' => $ex->getMessage(), 'code' => $ex->getCode(), 'args' => func_get_args(), 'trace' => $ex->getTraceAsString()];
            Log::channel('TikTokServers')->error('Api 服务异常', $errorData);
            return ['status_code' => $errorData['code'], 'reasonPhrase' => '', 'message' => $errorData['message'], 'errors' => $errorData];
//            Log::channel('TikTokServers')->error('Api 服务异常', ['msg' => $ex->getMessage(), 'code' => $ex->getCode(), 'args' => func_get_args(), 'trace' => $ex->getTraceAsString()]);
        }
    }

    /**
     * Notes: 请求结果解析
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:30
     * @param $response
     * @return mixed
     * @throws Exception
     */
    protected function getResponse($response)
    {
        if ($response) {
            $result    = (string)$response->getBody()->getContents();
            $code      = $response->getStatusCode();   // 200
            $reason    = $response->getReasonPhrase(); // OK
            $xml_check = strpos($result, '<?xml') !== false;
            if ($xml_check) {
                $xml_namespace = strpos($result, '<ns3:') !== false ? 'ns3' : 'ns2';
                $result        = simplexml_load_string($result, null, 0, $xml_namespace, true);
                $result        = json_encode($result);
            }
            $result = json_decode($result, TRUE);
            return empty($result) ? ['statusCode' => $code, 'reasonPhrase' => $reason] : array_merge($result, ['statusCode' => $code]);
        } else {
            return ['status_code' => 401, 'reasonPhrase' => '', 'message' => '没有回应'];
//            throw new Exception('没有回应');
        }
    }

    /**
     * Notes: 获取token
     * author: lfy
     * Date: 2021/11/19
     * Time: 14:31
     * @return array|mixed|void
     * @throws GuzzleException
     */
    public function getTokenData($uri = '/api/token/getAccessToken',$method=self::METHOD_POST)
    {
        $this->addApiParam('app_key', $this->appKey);
        $this->addApiParam('app_secret', $this->appSecret);
        $options = [
            RequestOptions::QUERY => $this->apiParams,
        ];
        return self::request($uri, $method, $options);
    }

    /**
     * Notes: GET请求方式
     * author: lfy
     * Date: 2021/9/25
     * Time: 20:57
     * @return bool|void
     * @throws GuzzleException
     */
    public function get($apiName, array $options = [])
    {
        $this->getOptions($apiName, $this->apiParams);
        $options = array_merge($options, [
            RequestOptions::QUERY => $this->commonParams,
        ]);
        return self::request($apiName, self::METHOD_GET, $options);
    }

    /**
     * Notes: POST请求
     * author: lfy
     * Date: 2021/9/25
     * Time: 20:57
     * @return bool|void
     * @throws GuzzleException
     */
    public function post($apiName, array $options = [])
    {
        $this->getOptions($apiName);
        $options = array_merge($options, [
            RequestOptions::QUERY => $this->commonParams,
            RequestOptions::JSON  => $this->apiParams,
        ]);
        return self::request($apiName, self::METHOD_POST, $options);
    }

    /**
     * Notes:
     * author: lfy
     * Date: 2021/10/11
     * Time: 11:23
     * @return mixed|void
     * @throws GuzzleException
     */
    public function put($apiName, array $options = [])
    {
        $this->getOptions($apiName, $this->apiParams);
        $options = array_merge($options, [
            RequestOptions::QUERY => http_build_query($this->commonParams),
            RequestOptions::JSON  => $this->apiParams,
        ]);
        return self::request($apiName, self::METHOD_PUT, $options);
    }

    /**
     * Notes:
     * author: lfy
     * Date: 2021/10/11
     * Time: 11:23
     * @return mixed|void
     * @throws GuzzleException
     */
    public function delete($apiName, array $options = [])
    {
        $this->getOptions($apiName);
        $options = array_merge($options, [
            RequestOptions::QUERY => $this->commonParams,
            RequestOptions::JSON  => $this->apiParams,
        ]);
        return self::request($apiName, self::METHOD_DELETE, $options);
    }

    /**
     * Notes:   请求参数组装
     * author: lfy
     * Date: 2021/10/7
     * Time: 10:28
     */
    private function getOptions($apiType, $params = []): void
    {
        $this->commonParams              = !empty($params) ? array_merge($params, $this->commonParams) : $this->commonParams;
        $this->commonParams['app_key']   = $this->appKey;
        $this->commonParams['timestamp'] = time();
        if ($this->version=='202309'){
            if (!empty($this->token))  $this->addHeaderParam('x-tts-access-token',$this->token);
            $type=2;
        }else{
            $type=1;

            if (!empty($this->token)) $this->commonParams['access_token'] = $this->token;
            if (!empty($this->shopId)) $this->commonParams['shop_id'] = $this->shopId;
        }
        $this->commonParams['sign'] = $this->getSign($this->commonParams, $apiType,$type);

    }

    /**
     * Notes: 生成签名
     * author: lfy
     * Date: 2021/11/19
     * Time: 13:46
     * @param $params
     * @param $key
     * @return string
     */
    public function getSign($params, $uri,$type=1): string
    {
        $secret      = $this->appSecret;
        $sign_string = '';
        if (!empty($params)) {
            $params = array_diff_key($params, ['sign' => '', 'access_token' => '']);
            ksort($params);
            if ($type==2 && !empty($this->apiParams)){
                $sign_string = $secret . $uri . $this->buildParameterString($params).json_encode($this->apiParams,350) . $secret;
            }else{
                $sign_string = $secret . $uri . $this->buildParameterString($params) . $secret;

            }
        }
        return hash_hmac('sha256', $sign_string, $secret);
    }

    private function buildParameterString($params): string
    {
        $string = '';
        foreach ($params as $key => $value) {
            if (!is_array($value)) $string .= $key . $value;
        }
        return $string;
    }
    /*
     *  请求基础设置结束
     */
}
